package com.lai.hospital.utils;

import com.lai.hospital.config.JwtProperties;
import com.lai.hospital.config.RedisConfig;
import com.lai.hospital.constant.SecurityConst;
import com.lai.hospital.dto.UserDetailDTO;
import com.lai.hospital.dto.UserInfoDTO;
import com.lai.hospital.exception.BizException;
import com.lai.hospital.service.RedisService;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.*;

import static com.lai.hospital.constant.SecurityConst.*;


/**
 * @author : lai
 * @version V1.0
 * @Project: hospital-springboot
 * @Package com.lai.hospital.dao
 * @Description: JWT工具类
 * @date Date : 2022年11月18日 13:16
 */
@Component
public class JwtTokenUtils {


    @Autowired
    RedisService redisService;

    @Autowired
    JwtProperties jwtProperties;

    //创建Token
    public String generateJwt(UserDetailDTO userDetailDTO, Boolean rememberMe){
        //添加头部信息
        Map<String,Object> header = new HashMap<>();
        header.put("typ",TOKEN_TYPE);//TYPE类型
        header.put("alg", TOKEN_ALG);//使用 HS256算法
        // 生成签名密钥
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(jwtProperties.getSecret());
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());

        //添加载荷信息
        Map<String,Object> claims = new HashMap<>();
        //可以自定义载荷信息
        claims.put("roles",userDetailDTO.getAuthorities());

        //生成JWT的时间
        long nowTime = System.currentTimeMillis();
        Date issuedAt = new Date(nowTime);

        //生成token信息
        JwtBuilder builder = Jwts.builder()
                .setHeader(header) // 设置头部信息
                .setClaims(claims) // 如果有私有声明，一定要先设置自己创建的这个私有声明，这是给builder的claim赋值，一旦写在标准的声明赋值之后，就是覆盖了那些标准的声明
                .setId(userDetailDTO.getId().toString()) // jti(JWT ID)：jwt的唯一身份标识，根据业务需要，可以设置为一个不重复的值，主要用来作为一次性token，从而回避重放攻击
                .setIssuedAt(issuedAt) // iat(issuedAt)：jwt的签发时间
                .setIssuer("MiKo") // iss(issuer)：jwt签发者
                .setSubject(userDetailDTO.getUsername()) // sub(subject)：jwt所面向的用户，放登录的用户名，一个json格式的字符串，可存放userid，roldid之类，作为用户的唯一标志
                .signWith(signatureAlgorithm, signingKey); // 设置签名，使用的是签名算法和签名使用的秘钥

        //设置过期时间
        long exp;
        if(rememberMe){
            exp = nowTime + SecurityConst.EXPIRATION_REMEMBER;
        }else {
            exp = nowTime + SecurityConst.EXPIRATION;
        }
        builder.setExpiration(new Date(exp));

        return builder.compact();

    }

    //解析Token
    public Claims parseJwt1(String token){
        try{
            return Jwts.parser()
                    .setSigningKey(jwtProperties.getSecret())
                    .parseClaimsJws(token)
                    .getBody();
        }catch(ExpiredJwtException expired){
            //过期
            System.out.println("====================过期");
//            throw new BizException(TOKEN_EXPIRE);
        }catch (SignatureException e){
            //无效
            System.out.println("====================无效");
//            throw new BizException(TOKEN_FAILURE);
        }catch(MalformedJwtException malformedJwt){
            //无效
            System.out.println("====================无效");
//            throw new BizException(TOKEN_FAILURE);
        }
        return null;
    }

    //解析Token
    public Claims parseJwt(String token){
        try{
            return Jwts.parser()
                    .setSigningKey(jwtProperties.getSecret())
                    .parseClaimsJws(token)
                    .getBody();
        }catch(ExpiredJwtException expired){
            //过期
            throw new BizException(TOKEN_EXPIRE);
        }catch (SignatureException e){
            //无效
            throw new BizException(TOKEN_FAILURE);
        }catch(MalformedJwtException malformedJwt){
            //无效
            throw new BizException(TOKEN_FAILURE);
        }
    }

    /**
     * 刷新令牌
     *
     * @param claims
     * @return
     */
    public String refreshToken(Claims claims) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        Map<String,Object> header = new HashMap<>();
        header.put("typ","JWT");//TYPE类型
        header.put("alg", "HS256");//使用 HS256算法
        // 生成签名密钥
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(jwtProperties.getSecret());
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());

        // 添加构成JWT的参数
        JwtBuilder builder = Jwts.builder()
                .setHeader(header) // 设置头部信息
                .setClaims(claims) // 如果有私有声明，一定要先设置自己创建的这个私有声明，这是给builder的claim赋值，一旦写在标准的声明赋值之后，就是覆盖了那些标准的声明
                .setId(claims.getId()) // jti(JWT ID)：jwt的唯一身份标识，根据业务需要，可以设置为一个不重复的值，主要用来作为一次性token，从而回避重放攻击
                .setIssuedAt(now) // iat(issuedAt)：jwt的签发时间
                .setIssuer(claims.getIssuer()) // iss(issuer)：jwt签发者
                .setSubject(claims.getSubject()) // sub(subject)：jwt所面向的用户，放登录的用户名，一个json格式的字符串，可存放userid，roldid之类，作为用户的唯一标志
                .signWith(signatureAlgorithm, signingKey); // 设置签名，使用的是签名算法和签名使用的秘钥

        // 添加Token过期时间
        if (SecurityConst.EXPIRATION >= 0) {
            long expMillis = nowMillis + SecurityConst.EXPIRATION;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp).setNotBefore(now);
        }

       return builder.compact();
    }

}



